#include <basic/errcode.h>
#include <basic/skt.h>

#define _SUN_PREFIX         "/tmp/"
#define _LOW_BUF_SIZE       KB(4)

enum _wait_option
{/*{{{*/
    _CONNECT_=1,
    _ACCEPT_,
    _RECV_,
    _SEND_,
};/*}}}*/

static SKT _create_skt(SOCKET sock, unsigned int listen,
        unsigned int local)
{/*{{{*/
    SKT skt;

    ALLOC_RETERR(skt, struct _skt, (SKT)NULL);

    skt->local=local;
    skt->listen=listen;
    CLR_BUF(skt->sock_info.self_ip);
    skt->sock_info.self_port=0;
    CLR_BUF(skt->sock_info.peer_ip);
    skt->sock_info.peer_port=0;
    skt->sock_info.recvbuf_size=-1;
    skt->sock_info.sendbuf_size=-1;
    skt->sock=sock;
    skt->_rbuf.begin=skt->_rbuf.end=0;

    if(listen)
    {
        skt->_rbuf.buf=(unsigned char *)NULL;
        skt->_rbuf.size=0;
        skt->_wbuf.buf=(unsigned char *)NULL;
        skt->_wbuf.size=0;
    }
    else
    {
        if(sock_get_info(sock, &skt->sock_info))
        {   free(skt); return((SKT)NULL);   }
        ALLOC_ARRAY_DOERR(skt->_rbuf.buf, unsigned char,
                skt->sock_info.recvbuf_size+_LOW_BUF_SIZE,
                skt->_rbuf.size,
                free(skt); return((SKT)NULL));
        ALLOC_ARRAY_DOERR(skt->_wbuf.buf, unsigned char, _LOW_BUF_SIZE,
                skt->_wbuf.size,
                free(skt->_rbuf.buf); free(skt); return((SKT)NULL));
    }

    return(skt);
}/*}}}*/

static int _wait(unsigned int type, SOCKET sock, int *p_timeout_ms,
        short *p_revents)
{/*{{{*/
    /*
    int timeout_ms;
    fd_set rset, wset;
    struct timeval tv;
    int res=0;
#ifdef __linux__
#else
    long long begin;
    int used;
#endif

    if(!p_timeout_ms || *p_timeout_ms < 0)
        return(0);
    timeout_ms=*p_timeout_ms;

    switch(type)
    {
        case _RECV_:
        case _ACCEPT_:
            FD_ZERO(&rset);
            FD_SET(sock, &rset);
            break;
        case _SEND_:
            FD_ZERO(&wset);
            FD_SET(sock, &wset);
            break;
        default:
            return(-1);
    }

AGAIN:
    tv.tv_sec=timeout_ms / 1000;
    tv.tv_usec=(timeout_ms % 1000) * 1000;
#ifdef __linux__
#else
    begin=curr_timestamp(NULL)*1000L;
#endif
    switch(type)
    {
        case _RECV_:
        case _ACCEPT_:
            res=select(sock+1, &rset, NULL, NULL, &tv);
            break;
        case _SEND_:
            res=select(sock+1, NULL, &wset, NULL, &tv);
            break;
    }
    if(res == SOCKET_ERROR)
    {
        if(get_last_error(NULL) == EINTR)
            goto AGAIN;
        return(-1);
    }
    if(res == 0)
        return(RET_ERR_TIMEOUT);

#ifdef __linux__
    *p_timeout_ms=tv.tv_sec*1000 + tv.tv_usec/1000;
#else
    used=curr_timestamp(NULL)*1000L - begin;
    *p_timeout_ms=timeout_ms > used ? timeout_ms - used : 0;
#endif
    */
    struct pollfd fd;
    int poll_num;
    struct timeval tv;
    long long begin;
    int used;

    if(p_revents)
        *p_revents=0;
    if(!p_timeout_ms || *p_timeout_ms < 0)
        return(0);

    fd.fd=sock;
    fd.events=fd.revents=0;
    switch(type)
    {
        case _CONNECT_:
            fd.events|=POLLIN; fd.events|=POLLOUT; break;
        case _ACCEPT_:
        case _RECV_:
            fd.events|=POLLIN; break;
        case _SEND_:
            fd.events|=POLLOUT; break;
    }

    gettimeofday(&tv, NULL);
    begin=tv.tv_sec*1000LL+tv.tv_usec/1000;

POLL_AGAIN:
    poll_num=poll(&fd, 1, *p_timeout_ms);
    if(poll_num == SOCKET_ERROR)
    {
        if(get_last_error(NULL) == EINTR)
            goto POLL_AGAIN;
        return(-1);
    }
    // 超时
    if(poll_num == 0)
        return(RET_ERR_TIMEOUT);

    gettimeofday(&tv, NULL);
    used=tv.tv_sec*1000LL+tv.tv_usec/1000 - begin;
    *p_timeout_ms=*p_timeout_ms > used ? *p_timeout_ms - used : 0;
    if(p_revents)
        *p_revents=fd.revents;

    return(0);
}/*}}}*/

static int _recv(SKT skt, int *p_timeout_ms)
{/*{{{*/
    int res;
    struct _skt_rbuf *rbuf=&skt->_rbuf;
    short revents;

    if((res=_wait(_RECV_, skt->sock, p_timeout_ms, &revents)))
    {
        //printf("%s: revents: 0x%03X\n", __func__, revents);
        return(res);
    }
    //printf("%s: revents: 0x%03X\n", __func__, revents);
AGAIN:
    if((res=recv(skt->sock, rbuf->buf+rbuf->end,
                    rbuf->size-rbuf->end, 0)) == SOCKET_ERROR)
    {
        if(get_last_error(NULL) == EINTR)
            goto AGAIN;
        return(-2);
    }
    if(res == 0)  // 套接口接收FIN
        return(RET_ERR_ABORT);
    rbuf->end+=res;
    return(res);
}/*}}}*/

static int _send(SKT skt, unsigned char *writebuf, int write_size,
        int *p_timeout_ms)
{/*{{{*/
    int res; 

    if((res=_wait(_SEND_, skt->sock, p_timeout_ms, NULL)))
        return(res);
AGAIN:
    if((res=send(skt->sock, writebuf, write_size, 0)) == SOCKET_ERROR)
    {
        if(get_last_error(NULL) == EINTR)
            goto AGAIN;
        return(-2);
    }
    return(res);
}/*}}}*/

int skt_connect(char *host_addr, unsigned int flag, int *p_timeout_ms,
        SKT *pskt)
{/*{{{*/
    SOCKET sock=INVALID_SOCKET;
    SKT skt;
    int rtn;

    if(!pskt) return(RET_ERR_PARA);
    *pskt=(SKT)NULL;

    if(flag & FLAG_LOCAL)    // unix域
    {
        struct sockaddr_un client_addr;
        int len;

        if(!host_addr || (len=strlen(host_addr)) <= 0 ||
                len > sizeof(client_addr.sun_path) -
                strlen(_SUN_PREFIX) - 1)
            return(RET_ERR_PARA);

        if((sock=socket(AF_UNIX, SOCK_STREAM, 0)) == INVALID_SOCKET)
        {   rtn=-1; goto OUT_ERR;   }
        memset(&client_addr, 0, sizeof(client_addr));
        client_addr.sun_family=AF_UNIX;
        strcpy(client_addr.sun_path, _SUN_PREFIX);
        strcat(client_addr.sun_path, host_addr);
        if(connect(sock, (const struct sockaddr *)&client_addr,
                    sizeof(client_addr)) == INVALID_SOCKET)
        {   rtn=-2; goto OUT_ERR;   }
    }
    else    // 套接字
    {
        char ip[IP_SIZE+1];
        int res;
        int flags;
        struct sockaddr_in client_addr, server_addr;
        //fd_set rset, wset;
        //struct timeval tv;
        struct host_addr_info addrinfo;

        if(host_addr_parse(host_addr, &addrinfo) || addrinfo.port == 0)
        {   rtn=-3; goto OUT_ERR;   }
        if((sock=socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
        {   rtn=-4; goto OUT_ERR;   }
        if(host_name_to_ip(BUF_IS_CLR(addrinfo.host)?
                    "localhost":addrinfo.host, ip) == NULL)
        {   rtn=-5; goto OUT_ERR;   }
        memset(&client_addr, 0, sizeof(client_addr));
        client_addr.sin_family=AF_INET;
        client_addr.sin_port=htons(addrinfo.port);
        if(inet_pton(AF_INET, ip, &client_addr.sin_addr) <= 0)
        {   rtn=-6; goto OUT_ERR;   }
        if((flags=fcntl(sock, F_GETFL, 0)) == -1 ||
                fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)
        {   rtn=-7; goto OUT_ERR;   }
        if((res=connect(sock, (const struct sockaddr *)&client_addr,
                        sizeof(client_addr))) == SOCKET_ERROR)
        {
            short revents;

            if(get_last_error(NULL) != EINPROGRESS)
            {   rtn=-8; goto OUT_ERR;   }
            /*
            FD_ZERO(&rset);
            FD_SET(sock, &rset);
            wset=rset;
AGAIN:
            tv.tv_sec=*p_timeout_ms / 1000;
            tv.tv_usec=(*p_timeout_ms % 1000) * 1000;
            if((res=select(sock+1, &rset, &wset, NULL,
                            (*p_timeout_ms >= 0) ? &tv : NULL)) ==
                    SOCKET_ERROR)
            {
                int errnum=get_last_error(NULL);
                if(errnum == EINTR)  goto AGAIN;
                {   rtn=-8; goto OUT_ERR;   }
            }
            if(res == 0)
            {   rtn=RET_ERR_TIMEOUT; goto OUT_ERR;   }
            if(FD_ISSET(sock, &rset) || FD_ISSET(sock, &wset))
            {
                socklen_t server_addr_len;
                memset(&server_addr, 0, sizeof(server_addr));
                server_addr_len=sizeof(server_addr);
                if(getpeername(sock, (struct sockaddr *)&server_addr,
                            &server_addr_len) == SOCKET_ERROR)
                {   rtn=-9; goto OUT_ERR;   }
            }
            else
            {   rtn=-10; goto OUT_ERR;   }
            */
            if((res=_wait(_CONNECT_, sock, p_timeout_ms, &revents)))
            {  rtn=res; goto OUT_ERR;   }
            //printf("%s: revents: 0x%03X\n", __func__, revents);
            if((revents & POLLIN) || (revents & POLLOUT))
            {
                socklen_t server_addr_len;
                memset(&server_addr, 0, sizeof(server_addr));
                server_addr_len=sizeof(server_addr);
                if(getpeername(sock, (struct sockaddr *)&server_addr,
                            &server_addr_len) == SOCKET_ERROR)
                {   rtn=-9; goto OUT_ERR;   }
            }
            else
            {   rtn=-10; goto OUT_ERR;   }
        }
        if(fcntl(sock, F_SETFL, flags) == -1)
        {   rtn=-11; goto OUT_ERR;   }
    }

    if(!(skt=_create_skt(sock, 0, (flag & FLAG_LOCAL)?1:0)))
    {   rtn=RET_ERR_ALLOC; goto OUT_ERR;   }

    *pskt=skt;
    return(0);

OUT_ERR:
    sock_close(sock);
    return(rtn);
}/*}}}*/

int skt_listen(char *port, unsigned int flag, SKT *pskt)
{/*{{{*/
    SOCKET sock=INVALID_SOCKET;
    SKT skt;
    int rtn;
    struct sockaddr_in addr_in;
    struct sockaddr_un addr_un;
    struct sockaddr *addr;
    socklen_t addr_len;

    if(!port || !pskt)
        return(RET_ERR_PARA);
    *pskt=NULL;

    if(flag & FLAG_LOCAL)
    {
        int len;
        if((len=strlen(port)) <= 0 ||
                len > sizeof(addr_un.sun_path)-
                strlen(_SUN_PREFIX)-1)
        {   rtn=RET_ERR_PARA; goto OUT_ERR;   }
        if((sock=socket(AF_UNIX, SOCK_STREAM, 0)) == INVALID_SOCKET)
        {   rtn=-1; goto OUT_ERR;   }
        memset(&addr_un, 0, sizeof(struct sockaddr_un));
        addr_un.sun_family=AF_UNIX;
        strcpy(addr_un.sun_path, _SUN_PREFIX);
        strcat(addr_un.sun_path, port);
        unlink(addr_un.sun_path);
        addr=(struct sockaddr *)&addr_un;
        addr_len=sizeof(struct sockaddr_un);
    }
    else
    {
        int optval;

        if((sock=socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
        {   rtn=-2; goto OUT_ERR;   }
        optval=1;
        if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
                    &optval, sizeof(optval)) == SOCKET_ERROR)
        {   rtn=-3; goto OUT_ERR;   }
        memset(&addr_in, 0, sizeof(struct sockaddr_in));
        addr_in.sin_family=AF_INET;
        addr_in.sin_addr.s_addr=htonl(INADDR_ANY);
        addr_in.sin_port=htons(atoi(port));
        addr=(struct sockaddr *)&addr_in;
        addr_len=sizeof(struct sockaddr_in);
    }
    if(bind(sock, addr, addr_len) == SOCKET_ERROR)
    {   rtn=-4; goto OUT_ERR;   }
    if(listen(sock, 1024) == SOCKET_ERROR)
    {   rtn=-5; goto OUT_ERR;   }

    if(!(skt=_create_skt(sock, 1, (flag & FLAG_LOCAL)?1:0)))
    {   rtn=RET_ERR_ALLOC; goto OUT_ERR;   }

    *pskt=skt;
    return(0);

OUT_ERR:
    sock_close(sock);
    return(rtn);
}/*}}}*/

int skt_accept(SKT listen_skt, int *p_timeout_ms, SKT *pskt)
{/*{{{*/
    int res;
    int rtn=0;
    SOCKET sock;
    struct sockaddr_un addr_un;
    struct sockaddr_in addr_in;
    struct sockaddr *addr;
    socklen_t addr_len;
    SKT skt;
    short revents;

    if(!pskt) return(RET_ERR_PARA);
    *pskt=NULL;
    if(!listen_skt) return(RET_ERR_PARA);

    if((res=_wait(_ACCEPT_, listen_skt->sock, p_timeout_ms, &revents)))
    {
        //printf("%s: revents: 0x%03X\n", __func__, revents);
        return(res);
    }
    //printf("%s: revents: 0x%03X\n", __func__, revents);

AGAIN:
    if(listen_skt->local)
    {
        addr=(struct sockaddr *)&addr_un;
        addr_len=sizeof(struct sockaddr_un);
    }
    else
    {
        addr=(struct sockaddr *)&addr_in;
        addr_len=sizeof(struct sockaddr_in);
    }
    if((sock=accept(listen_skt->sock, addr, &addr_len)) == INVALID_SOCKET)
    {
        int errnum=get_last_error(NULL);
        if(errnum == ECONNABORTED || errnum == EINTR)
            goto AGAIN;
        return(-2);
    }

    if(!(skt=_create_skt(sock, 0, listen_skt->local)))
    {   rtn=RET_ERR_ALLOC; goto OUT_ERR;   }

    *pskt=skt;
    return(0);

OUT_ERR:
    sock_close(sock);
    return(rtn);
}/*}}}*/

void skt_close(SKT skt)
{/*{{{*/
    if(skt)
    {
        if(skt->sock > 0)
            close(skt->sock);
        FREE(skt->_rbuf.buf);
        FREE(skt->_wbuf.buf);
        free(skt);
    }
}/*}}}*/

int skt_pair(SKT sktpair[2])
{/*{{{*/
    int rtn=0;
    SOCKET sockpair[2];

    if(!sktpair) return(RET_ERR_PARA);
    sktpair[0]=sktpair[1]=(SKT)NULL;

    if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair) == SOCKET_ERROR)
        return(-1);
    if((sktpair[0]=_create_skt(sockpair[0], 0, 1)) == NULL ||
            (sktpair[1]=_create_skt(sockpair[1], 0, 1)) == NULL)
    {   rtn=RET_ERR_ALLOC; goto OUT_ERR;   }

    return(0);

OUT_ERR:
    if(sktpair[0])
    {
        FREE(sktpair[0]->_rbuf.buf);
        FREE(sktpair[0]->_wbuf.buf);
        free(sktpair[0]);
    }
    if(sktpair[1])
    {
        FREE(sktpair[1]->_rbuf.buf);
        FREE(sktpair[1]->_wbuf.buf);
        free(sktpair[1]);
    }
    sock_close(sockpair[0]);
    sock_close(sockpair[1]);
    return(rtn);
}/*}}}*/

int skt_readbuf_size(SKT skt)
{/*{{{*/
    if(!skt)
        return(RET_ERR_PARA);
    return(skt->_rbuf.end - skt->_rbuf.begin);
}/*}}}*/

static void _reset_rbuf(SKT skt)
{/*{{{*/
    struct _skt_rbuf *rbuf=&skt->_rbuf;

    if(rbuf->begin == rbuf->end)
    {   rbuf->begin=rbuf->end=0;   }
    else
    {
        if(rbuf->begin != 0)
        {
            int have;
            have=rbuf->end - rbuf->begin;
            memmove(rbuf->buf, rbuf->buf+rbuf->begin, have);
            rbuf->begin=0;
            rbuf->end=have;
        }
    }
}/*}}}*/

static int _read_size(SKT skt, int need, int *p_timeout_ms)
{/*{{{*/
    struct _skt_rbuf *rbuf=&skt->_rbuf;
    int res;
    int toread;

    if(need <= 0 || need > SOCK_MSG_SIZE)
       return(RET_ERR_PARA);

    toread=need - (rbuf->end - rbuf->begin);
    if(toread > 0)
    {
        if(toread > rbuf->size - rbuf->end)
        {
            EXPAND_ARRAY_DOERR(rbuf->buf, unsigned char,
                    toread - (rbuf->size - rbuf->end),
                    rbuf->size,
                    return(RET_ERR_ALLOC));
        }
        while(toread > 0)
        {
            if((res=_recv(skt, p_timeout_ms)) < 0)
                return(res);
            toread-=res;
        }
    }
    return(need);
}/*}}}*/

static int _read_wrap(SKT skt, int *p_timeout_ms)
{/*{{{*/
    struct _skt_rbuf *rbuf=&skt->_rbuf;
    int res;
    char buf[10];
    int have;
    int toread;
    int need;

    while((have=rbuf->end - rbuf->begin) < MSG_SIZE_SIZE)
    {
        if((res=_recv(skt, p_timeout_ms)) < 0)
            return(res);
    }
    memcpy(buf, rbuf->buf+rbuf->begin, MSG_SIZE_SIZE);
    buf[MSG_SIZE_SIZE]=ZERO;
    rbuf->begin+=MSG_SIZE_SIZE;
    need=strtol(buf, NULL, 16);
    if(need < 0 || need > SOCK_MSG_SIZE)
       return(-3);

    toread=need - (rbuf->end - rbuf->begin);
    if(toread > 0)
    {
        if(toread > rbuf->size - rbuf->end)
        {
            EXPAND_ARRAY_DOERR(rbuf->buf, unsigned char,
                    toread - (rbuf->size - rbuf->end),
                    rbuf->size,
                    return(RET_ERR_ALLOC));
        }
        while(toread > 0)
        {
            if((res=_recv(skt, p_timeout_ms)) < 0)
                return(res);
            toread-=res;
        }
    }
    return(need);
}/*}}}*/

static int _read_line(SKT skt, int *p_timeout_ms)
{/*{{{*/
    struct _skt_rbuf *rbuf=&skt->_rbuf;
    char *p;
    int res;

    rbuf->buf[rbuf->end]=ZERO;
    while((p=strstr((char *)(rbuf->buf+rbuf->begin), RTNS)) == NULL)
    {
        if(rbuf->size == SOCK_MSG_SIZE)
            return(-4);
        if(rbuf->size - rbuf->end < _LOW_BUF_SIZE)
        {
            int margin=SOCK_MSG_SIZE - rbuf->size;
            EXPAND_ARRAY_DOERR(rbuf->buf, unsigned char,
                    (margin < _LOW_BUF_SIZE) ? margin : _LOW_BUF_SIZE,
                    rbuf->size, return(RET_ERR_ALLOC)); 
        }
        if((res=_recv(skt, p_timeout_ms)) < 0)
            return(res);
        rbuf->buf[rbuf->end]=ZERO;
    }
    return(p + strlen(RTNS) - (char *)(rbuf->buf + rbuf->begin));
}/*}}}*/

static int _read_text(SKT skt, int *p_timeout_ms)
{/*{{{*/
    struct _skt_rbuf *rbuf=&skt->_rbuf;
    char *p;
    int res;

    rbuf->buf[rbuf->end]=ZERO;
    while(!(p=strstr((char *)(rbuf->buf+rbuf->begin), TEXTEND)))
    {
        if(rbuf->size == SOCK_MSG_SIZE)
            return(-4);
        if(rbuf->size - rbuf->end < _LOW_BUF_SIZE)
        {
            int margin=SOCK_MSG_SIZE - rbuf->size;
            EXPAND_ARRAY_DOERR(rbuf->buf, unsigned char,
                    (margin < _LOW_BUF_SIZE) ? margin : _LOW_BUF_SIZE,
                    rbuf->size, return(RET_ERR_ALLOC)); 
        }
        if((res=_recv(skt, p_timeout_ms)) < 0)
            return(res);
        rbuf->buf[rbuf->end]=ZERO;
    }
    return(p - (char *)(rbuf->buf + rbuf->begin));
}/*}}}*/

static int _read_raw(SKT skt, int *p_timeout_ms)
{/*{{{*/
    int res;

    if((res=_recv(skt, p_timeout_ms)) < 0)
        return(res);
    return(skt->_rbuf.end - skt->_rbuf.begin);
}/*}}}*/

int skt_read(SKT skt, unsigned char *readbuf, int *read_size,
        unsigned int flag, int *p_timeout_ms)
{/*{{{*/
    int nread;

    if(!skt || !readbuf || !read_size)
        return(RET_ERR_PARA);

    switch(flag)
    {
        case SKT_R_SIZE:
            nread=_read_size(skt, *read_size, p_timeout_ms);
            break;
        case SKT_R_WRAP:
            nread=_read_wrap(skt, p_timeout_ms);
            break;
        case SKT_R_LINE:
            nread=_read_line(skt, p_timeout_ms);
            break;
        case SKT_R_TEXT:
            nread=_read_text(skt, p_timeout_ms);
            break;
        case 0:
        case SKT_R_RAW:
            nread=_read_raw(skt, p_timeout_ms);
            break;
        default:
            return(RET_ERR_PARA);
    }
    *read_size=0;
    CLR_BUF(readbuf);
    if(nread >= 0)
    {
        struct _skt_rbuf *rbuf=&skt->_rbuf;

        // 填充缓冲区
        memcpy(readbuf, rbuf->buf+rbuf->begin, nread);
        readbuf[nread]=ZERO;
        rbuf->begin+=nread;
        if(flag == SKT_R_TEXT)
            rbuf->begin+=strlen(TEXTEND);
        *read_size=nread;

        // 调整缓冲区
        _reset_rbuf(skt);
    }
    return(nread);
}/*}}}*/

int skt_read_alloc(SKT skt, unsigned char **preadbuf, int *read_size,
        unsigned int flag, int *p_timeout_ms)
{/*{{{*/
    int nread;

    if(!skt || !preadbuf || !read_size)
        return(RET_ERR_PARA);

    switch(flag)
    {
        case SKT_R_SIZE:
            nread=_read_size(skt, *read_size, p_timeout_ms);
            break;
        case SKT_R_WRAP:
            nread=_read_wrap(skt, p_timeout_ms);
            break;
        case SKT_R_LINE:
            nread=_read_line(skt, p_timeout_ms);
            break;
        case SKT_R_TEXT:
            nread=_read_text(skt, p_timeout_ms);
            break;
        case 0:
        case SKT_R_RAW:
            nread=_read_raw(skt, p_timeout_ms);
            break;
        default:
            return(RET_ERR_PARA);
    }
    *read_size=0;
    *preadbuf=NULL;
    if(nread >= 0)
    {
        struct _skt_rbuf *rbuf=&skt->_rbuf;
        unsigned char *readbuf;

        ALLOC_MULTI_RETERR(readbuf, unsigned char, nread+1, RET_ERR_PARA);

        // 填充缓冲区
        memcpy(readbuf, rbuf->buf+rbuf->begin, nread);
        readbuf[nread]=ZERO;
        rbuf->begin+=nread;
        if(flag == SKT_R_LINE)
            rbuf->begin+=strlen(CRLF);
        else if(flag == SKT_R_TEXT)
            rbuf->begin+=strlen(CRLF"."CRLF);
        *read_size=nread;
        *preadbuf=readbuf;

        // 调整缓冲区
        _reset_rbuf(skt);
    }
    return(nread);
}/*}}}*/

static int _write_size(SKT skt, unsigned char *writebuf, int write_size,
        int *p_timeout_ms)
{/*{{{*/
    if(write_size > 0)
    {
        unsigned char *p;
        int towrite;
        int res;

        towrite=write_size;
        p=writebuf;
        while(towrite > 0)
        {
            if((res=_send(skt, p, towrite, p_timeout_ms)) < 0)
                return(res);
            towrite-=res;
            p+=res;
        }
        return(write_size);
    }
    else if(write_size == 0)
        return(0);
    else
        return(RET_ERR_PARA);
}/*}}}*/

static int _write_wrap(SKT skt, unsigned char *writebuf, int write_size,
        int *p_timeout_ms)
{/*{{{*/
    if(write_size >= 0)
    {
        struct _skt_wbuf *wbuf=&skt->_wbuf;
        unsigned char *ptr;
        int towrite;
        int res;

        if(wbuf->size < write_size+MSG_SIZE_SIZE)
        {
            EXPAND_ARRAY_RETERR(wbuf->buf, unsigned char,
                    write_size + MSG_SIZE_SIZE - wbuf->size,
                    wbuf->size, RET_ERR_ALLOC);
        }
        sprintf((char *)wbuf->buf, SHORT_NUMBER_FMT, write_size);
        memcpy(wbuf->buf+SHORT_NUMBER_SIZE, writebuf, write_size);
        towrite=MSG_SIZE_SIZE+write_size;
        ptr=wbuf->buf;
        while(towrite > 0)
        {
            if((res=_send(skt, ptr, towrite, p_timeout_ms)) < 0)
                return(res);
            towrite-=res;
            ptr+=res;
        }
        return(MSG_SIZE_SIZE+write_size);
    }
    else
        return(RET_ERR_PARA);
}/*}}}*/

//static int _write_line(SKT skt, unsigned char *writebuf, int write_size,
//        int *p_timeout_ms)
//{/*{{{*/
//    if(write_size >= 0)
//    {
//        struct _skt_wbuf *wbuf=&skt->_wbuf;
//        int CRLF_len=strlen(CRLF);
//        unsigned char *ptr;
//        int towrite;
//        int res;
//
//        if(wbuf->size < write_size+CRLF_len)
//        {
//            EXPAND_ARRAY_RETERR(wbuf->buf, unsigned char,
//                    write_size + CRLF_len - wbuf->size,
//                    wbuf->size, RET_ERR_ALLOC);
//        }
//        memcpy(wbuf->buf, writebuf, write_size);
//        memcpy(wbuf->buf+write_size, CRLF, CRLF_len); 
//        towrite=write_size+CRLF_len;
//        ptr=wbuf->buf;
//        while(towrite > 0)
//        {
//            if((res=_send(skt, ptr, towrite, p_timeout_ms)) < 0)
//                return(res);
//            towrite-=res;
//            ptr+=res;
//        }
//        return(write_size+CRLF_len);
//    }
//    else
//        return(RET_ERR_PARA);
//}/*}}}*/

int skt_write(SKT skt, unsigned char *writebuf, int *write_size,
        unsigned int flag, int *p_timeout_ms)
{/*{{{*/
    int nwrite;

    if(!skt || !writebuf || !write_size)
        return(RET_ERR_PARA);

    switch(flag)
    {
        case 0:
        case SKT_W_SIZE:
            nwrite=_write_size(skt, writebuf, *write_size, p_timeout_ms);
            break;
        case SKT_W_WRAP:
            nwrite=_write_wrap(skt, writebuf, *write_size, p_timeout_ms);
            break;
        //case SKT_W_LINE:
        //    nwrite=_write_line(skt, writebuf, *write_size, p_timeout_ms);
        //    break;
        default:
            return(RET_ERR_PARA);
    }
    if(nwrite >= 0)
        *write_size=nwrite;
    return(nwrite);
}/*}}}*/

int skt_call(SKT skt,
        unsigned char *writebuf, int *writesize, unsigned int wflag,
        unsigned char *readbuf, int *readsize, unsigned int rflag,
        int *p_timeout_ms)
{/*{{{*/
    int res;

    if((res=skt_write(skt, writebuf, writesize, wflag, p_timeout_ms)) < 0)
        return(res);
    if((res=skt_read(skt, readbuf, readsize, rflag, p_timeout_ms)) < 0)
    {
        if(res > RET_ERR_PARA)
            res+=-2;
        return(res);
    }
    return(0);
}/*}}}*/
